﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;

namespace Velocity.Ink
{
    public class Signature : Control
    {
        // gdi objects
		Bitmap _bmp;
		Graphics _graphics;
		Pen _pen = new Pen(Color.Black);

		// list of line segments
		ArrayList _lines = new ArrayList();
		
		// the current line segment
		ArrayList _points = new ArrayList();
		Point _lastPoint = new Point(0,0);
		
		// if drawing signature or not
		bool _collectPoints = false;

		// notify parent that line segment was updated
		public event EventHandler SignatureUpdate;

        public Signature()
        {

        }

		/// <summary>
		/// List of signature line segments.
		/// </summary>
		public ArrayList Lines
		{
			get { return _lines; }
		}

        /// <summary>
        /// Gets or sets the BackgroundImage.
        /// </summary>
        /// <value>The BackgroundImage.</value>
        public Bitmap BackgroundImage 
        {
            get { return _bmp; }
            set 
            { 
                _bmp = value;
                this.Clear();
            }
        }

		/// <summary>
		/// Return the signature flattened to a stream of bytes.
		/// </summary>
		public byte[] SignatureBits
		{
			get { return SignatureData.GetBytes(this.Width, this.Height, _lines); }
		}

		protected override void OnPaint(PaintEventArgs e) 
		{
			// blit the memory bitmap to the screen
			// we draw on the memory bitmap on mousemove so there
			// is nothing else to draw at this time (the memory 
			// bitmap already contains all of the lines)
			CreateGdiObjects();
			e.Graphics.DrawImage(_bmp, 0, 0);
		}

		protected override void OnPaintBackground(PaintEventArgs e) 
		{
			// don't pass to base since we paint everything, avoid flashing
		}

		protected override void OnMouseDown(MouseEventArgs e) 
		{
			base.OnMouseDown(e);

			// process if currently drawing signature
			if (!_collectPoints)
			{
				// start collecting points
				_collectPoints = true;
				
				// use current mouse click as the first point
				_lastPoint.X = e.X;
				_lastPoint.Y = e.Y;

				// this is the first point in the list
				_points.Clear();
				_points.Add(_lastPoint);
			}
		}

		protected override void OnMouseUp(MouseEventArgs e) 
		{
			base.OnMouseUp(e);

			// process if drawing signature
			if (_collectPoints)
			{
				// stop collecting points
				_collectPoints = false;		

				// add current line to list of segments
				Point[] points = new Point[_points.Count];
				for (int i=0; i < _points.Count; i++)
				{
					Point pt = (Point)_points[i];
					points[i].X = pt.X;
					points[i].Y = pt.Y;
				}
				
				_lines.Add(points);

				// start over with a new line
				_points.Clear();
				
				// notify container a new segment was added
				RaiseSignatureUpdateEvent();
			}				
		}
	
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);			

			// process if drawing signature
			if (_collectPoints)
			{
				// add point to current line segment
				_points.Add(new Point(e.X, e.Y));

				// draw the new segment on the memory bitmap
				_graphics.DrawLine(_pen, _lastPoint.X, _lastPoint.Y, e.X, e.Y);
				
				// update the current position
				_lastPoint.X = e.X;
				_lastPoint.Y = e.Y;

				// display the updated bitmap
				Invalidate();
			}	
		}

		/// <summary>
		/// Clear the signature.
		/// </summary>
		public void Clear()
		{
			_lines.Clear();
            _bmp = null;
            this.CreateGdiObjects();
		}

		/// <summary>
		/// Create any GDI objects required to draw signature.
		/// </summary>
		private void CreateGdiObjects()
		{
			// only create if don't have one or the size changed
			if (_bmp == null || _bmp.Width != this.Width || _bmp.Height != this.Height)
			{
				// memory bitmap to draw on
				InitMemoryBitmap();
                this.Width = _bmp.Width;
                this.Height = _bmp.Height;
			}
            Invalidate();
		}
		
		/// <summary>
		/// Create a memory bitmap that is used to draw the signature.
		/// </summary>
		private void InitMemoryBitmap()
		{
            if (_bmp == null)
                _bmp = Internals.LoadImage("signhere.png");
            else
                _bmp = this.BackgroundImage;

			// get graphics object now to make drawing during mousemove faster
			_graphics = Graphics.FromImage(_bmp);           
		}
		
		/// <summary>
		/// Notify container that a line segment has been added.
		/// </summary>
		private void RaiseSignatureUpdateEvent()
		{
			if (this.SignatureUpdate != null)
				SignatureUpdate(this, EventArgs.Empty);
		}

        public string Serialize()
        {
            string str = string.Empty;

            // loop through all line segments
            for (int i = 0; i < this.Lines.Count; i++)
            {
                // get points for this line segment
                Point[] points = (Point[])this.Lines[i];
                foreach (Point pt in points)
                {
                    str += pt.X.ToString() + ":" + pt.Y.ToString() + ",";
                }
                if (str.EndsWith(",") == true)
                    str = str.Substring(0, str.Length - 1);

                str += ";";
            }
            if (str.EndsWith(";") == true)
                str = str.Substring(0, str.Length - 1);


            return GZipHelper.Compress(str);
        }

        public void DeSerialize(string str)
        {
            if (string.IsNullOrEmpty(str) == true)
                throw new ArgumentNullException("str");

            List<Point> arrP;
            
            // Decrypt
            str = GZipHelper.Decompress(str);

            string[] splitted = str.Split(";".ToCharArray());
            foreach (string item in splitted)
            {
                arrP = new List<Point>();

                string[] splitted2 = item.Split(",".ToCharArray());
                foreach (string item2 in splitted2)
                {
                    string[] p = item2.Split(":".ToCharArray());
                    Point pt = new Point();
                    pt.X = Convert.ToInt32(p[0]);
                    pt.Y = Convert.ToInt32(p[1]);

                    arrP.Add(pt);
                }
                this.Lines.Add(arrP.ToArray());
            }

            Graphics g = Graphics.FromImage(_bmp);
            foreach (Point[] points in this.Lines)
            {
                g.DrawLines(new Pen(Color.Black), points);
            }
            Invalidate();
        }
    }
}
